package campus.aiit.ssl.manage.modbus;

import campus.aiit.ssl.manage.modbus.provider.misc.ByteUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * communicate with device by TCP(socket)
 *
 * @author ihaokun
 * @date 2019/12/17 11:43
 */
@Component
@Slf4j
public class SocketForModBus {
    /**
     * Socket Data Unit, SDU(host, port, ADU(address, PDU(functionCode, Data), Error Check))
     */
    @Getter
    @Setter
    public static class SDU {
        private String host;
        private int port;
        private ModBus.ADU adu;

        public SDU() {
        }

        public SDU(String host, int port) {
            this.host = host;
            this.port = port;
        }
    }

    @Transactional
    public List<Integer> perform(SDU sdu,String info) {
        Socket session = connect(sdu.getHost(), sdu.getPort());
        Objects.requireNonNull(session);
        sendMessage(session, sdu.getAdu());
        if (sdu.getAdu().getAddress().equals("00")) {// 为广播发布，无应答（地址码为00）
            log.warn("Additional Address is 00, no response");
            return null;
        } else {
            return receiveMessage(session,info);
        }
    }

    private Socket connect(String host, int port) {
        Socket socket = new Socket();
        SocketAddress socketAddress = new InetSocketAddress(host, port);
        try {
            socket.connect(socketAddress, 2000); // 连不上的?毫秒断掉连接
            return socket;
        } catch (IOException e) {
            log.error("socket connect failed,socket连接失败，查看设备端口，ip是否通信正常");
            return socket;
        }
    }

    private void sendMessage(Socket session, ModBus.ADU adu) {
        String message = adu.toString();

        byte[] writeBytes = ByteUtils.hexStringToBytes(message);

        System.out.println("发送指令,功能码：" + adu.getPdu().getFunCode() + "完整数据报文："+message);

        OutputStream os = null;
        try {
            os = session.getOutputStream();
            os.write(writeBytes);
        } catch (IOException e) {
            log.error("Socket getOutputStream/write exception======sendMessage方法");
        }
    }

    private List<Integer> receiveMessage(Socket session,String info) {
        List<Integer> bytes = new ArrayList<>();
        DataInputStream dis;
        try {
            session.setSoTimeout(2000); // 设置socket读超时时间?毫秒
            dis = new DataInputStream(session.getInputStream());
        } catch (IOException e) {
            System.err.println("Get InputStream Failed========receiveMessage方法");
            return bytes;
        }
        try {
            int data;
            while ((data = dis.read()) != -1) {
                bytes.add(data);
            }
        } catch (IOException e) {
            if (e.getClass() != SocketTimeoutException.class) {
                e.printStackTrace();
            }
        } finally {
            try {
                dis.close();
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // int length = dis.available();
        // //XXX available() : run mode is 0, but debug mode is not 0; because it get length in non-block
        // System.out.println(length);
        // for (int i = 0; i < length; i++) {
        //   bytes.add(dis.readUnsignedByte());
        // }
        System.out.println("接收数据"+info+":"+ Arrays.asList(bytes.toArray()));
        return bytes;
    }

}